import Axios, { type AxiosInstance, type AxiosRequestConfig, type CustomParamsSerializer } from "axios";
import type { PureHttpError, RequestMethods, PureHttpResponse, PureHttpRequestConfig } from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
import { ElMessageBox } from "element-plus";
import router from "@/router";
import { message } from "../message";

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  // 请求超时时间
  timeout: 10000,
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest"
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: {
    serialize: stringify as unknown as CustomParamsSerializer
  }
};

class PureHttp {
  constructor() {
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }

  /** `token`过期后，暂存待执行的请求 */
  private static requests = [];

  /** 防止重复刷新`token` */
  private static isRefreshing = false;

  /** 防止重复弹出 401 对话框 */
  private static isShowingAuthDialog = false;

  /** 初始化配置对象 */
  private static initConfig: PureHttpRequestConfig = {};

  /** 保存当前`Axios`实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

  /** 重连原始请求 */
  private static retryOriginalRequest(config: PureHttpRequestConfig) {
    return new Promise(resolve => {
      PureHttp.requests.push((token: string) => {
        config.headers["Authorization"] = formatToken(token);
        resolve(config);
      });
    });
  }

  /** 请求拦截 */
  private httpInterceptorsRequest(): void {
    PureHttp.axiosInstance.interceptors.request.use(
      async (config: PureHttpRequestConfig): Promise<any> => {
        // 开启进度条动画
        NProgress.start();
        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof config.beforeRequestCallback === "function") {
          config.beforeRequestCallback(config);
          return config;
        }
        if (PureHttp.initConfig.beforeRequestCallback) {
          PureHttp.initConfig.beforeRequestCallback(config);
          return config;
        }
        /** 请求白名单，放置一些不需要`token`的接口（通过设置请求白名单，防止`token`过期后再请求造成的死循环问题） */
        const whiteList = ["/refresh-token", "/login"];
        // prettier-ignore
        return whiteList.some(url => config.url.endsWith(url))
          ? config
          : new Promise(resolve => {
            const data = getToken();
            // console.log('getToken', data)
            if (data) {
              const now = new Date().getTime();
              const expired = parseInt(data.expires) - now <= 0;
              // console.log('expired', expired)
              if (expired) {
                if (!PureHttp.isRefreshing) {
                  PureHttp.isRefreshing = true;
                  // token过期刷新
                  useUserStoreHook()
                    .handRefreshToken({ refreshToken: data.refreshToken })
                    .then(res => {
                      const token = res.data.accessToken || res.accessToken;
                      config.headers["Authorization"] = formatToken(token);
                      PureHttp.requests.forEach(cb => cb(token));
                      PureHttp.requests = [];
                    })
                    .finally(() => {
                      PureHttp.isRefreshing = false;
                    });
                }
                resolve(PureHttp.retryOriginalRequest(config));
              } else {
                config.headers["Authorization"] = formatToken(data.accessToken);
                // console.log('headers', formatToken(data.accessToken))
                resolve(config);
              }
            } else {
              resolve(config);
            }
          });
      },
      error => {
        return Promise.reject(error);
      }
    );
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    const instance = PureHttp.axiosInstance;
    instance.interceptors.response.use(
      (response: PureHttpResponse) => {
        const $config = response.config;
        // 关闭进度条动画
        NProgress.done();

        // 处理响应数据中的业务错误码
        const handleBusinessError = (data: any) => {
          if (data && typeof data === "object" && "code" in data) {
            const code = data.code;

            // 成功状态码
            if (code === 200 || code === 201) {
              return data;
            }

            // 401 未授权 - 弹窗确认后跳转登录
            if (code === 401) {
              // 防止重复弹窗
              if (!PureHttp.isShowingAuthDialog) {
                PureHttp.isShowingAuthDialog = true;

                ElMessageBox.confirm(data.message || "登录已过期，请重新登录", "登录过期提示", {
                  confirmButtonText: "重新登录",
                  cancelButtonText: "取消",
                  type: "warning",
                  closeOnClickModal: false
                })
                  .then(() => {
                    // 用户点击确定，清除登录信息并跳转（不调用接口，避免重复 401）
                    useUserStoreHook().clearAuthAndRedirect();
                  })
                  .catch(() => {
                    // 用户点击取消，不做处理
                    console.log("用户取消重新登录");
                  })
                  .finally(() => {
                    PureHttp.isShowingAuthDialog = false;
                  });
              }
              return Promise.reject(data);
            }

            // 403 禁止访问 - 跳转到 403 页面
            if (code === 403) {
              console.warn("403 权限不足，跳转到 403 页面");
              // router.push("/error/403");
              return Promise.reject(data);
            }

            // 500 服务器错误 - 跳转到 500 页面
            if (code === 500) {
              console.error("500 服务器内部错误，跳转到 500 页面");
              router.push("/error/500");
              return Promise.reject(data);
            }

            // 其他错误码
            return Promise.reject(data);
          }
          return data;
        };

        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof $config.beforeResponseCallback === "function") {
          $config.beforeResponseCallback(response);
          return handleBusinessError(response.data);
        }
        // 优先执行初始化设置等回调
        if (PureHttp.initConfig.beforeResponseCallback) {
          PureHttp.initConfig.beforeResponseCallback(response);
          return handleBusinessError(response.data);
        }

        return handleBusinessError(response.data);
      },
      (error: PureHttpError) => {
        const $error = error;
        $error.isCancelRequest = Axios.isCancel($error);
        // 关闭进度条动画
        NProgress.done();

        // 处理 HTTP 状态码错误
        if (error.response) {
          const status = error.response.status;

          switch (status) {
            case 401:
              // 防止重复弹窗
              if (!PureHttp.isShowingAuthDialog) {
                PureHttp.isShowingAuthDialog = true;

                ElMessageBox.confirm("登录已过期，请重新登录", "登录过期提示", {
                  confirmButtonText: "重新登录",
                  cancelButtonText: "取消",
                  type: "warning",
                  closeOnClickModal: false
                })
                  .then(() => {
                    // 用户点击确定，清除登录信息并跳转（不调用接口，避免重复 401）
                    useUserStoreHook().clearAuthAndRedirect();
                  })
                  .catch(() => {
                    // 用户点击取消
                    console.log("用户取消重新登录");
                  })
                  .finally(() => {
                    PureHttp.isShowingAuthDialog = false;
                  });
              }
              break;
            case 403:
              console.warn("HTTP 403 权限不足，跳转到 403 页面");
              break;
            case 404:
              console.warn("HTTP 404 请求的资源不存在，跳转到 404 页面");
              break;
            case 500:
              console.error("HTTP 500 服务器内部错误，跳转到 500 页面");
              message("服务器发生错误，请稍后重试", { type: "error" });
              router.push("/error/500");
              break;
            case 502:
              console.error("HTTP 502 网关错误");
              break;
            case 503:
              console.error("HTTP 503 服务不可用");
              break;
            default:
              console.error(`HTTP ${status} 错误`);
          }
        }

        // 所有的响应异常 区分来源为取消请求/非取消请求
        return Promise.reject($error);
      }
    );
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: PureHttpRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as PureHttpRequestConfig;

    // 单独处理自定义请求/响应回调
    return new Promise((resolve, reject) => {
      PureHttp.axiosInstance
        .request(config)
        .then((response: undefined) => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  /** 单独抽离的`post`工具函数 */
  public post<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
    return this.request<T>("post", url, params, config);
  }

  /** 单独抽离的`get`工具函数 */
  public get<T, P>(url: string, params?: AxiosRequestConfig<P>, config?: PureHttpRequestConfig): Promise<T> {
    return this.request<T>("get", url, params, config);
  }
}

export const http = new PureHttp();
